package vca;

import java.awt.Point;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;

import jenes.AlgorithmEventListener;
import jenes.GenerationEventListener;
import jenes.GeneticAlgorithm;
import jenes.chromosome.IntegerChromosome;
import jenes.population.Fitness;
import jenes.population.Individual;
import jenes.population.Population;
import jenes.population.Population.Statistics.Group;
import jenes.stage.AbstractStage;
import jenes.stage.operator.Crossover;
import jenes.stage.operator.Mutator;
import jenes.stage.operator.common.OnePointCrossover;
import jenes.stage.operator.common.SimpleMutator;
import jenes.stage.operator.common.TournamentSelector;
import jenes.tutorials.utils.Utils;

public class GA {

	private int POPULATION_SIZE;
	private int CHROMOSOME_LENGTH;
	private int GENERATION_LIMIT;

	private GraphManager gm;
	private GeneticAlgorithm<IntegerChromosome> ga;

	public GA(final GraphManager gm, int chromLenght, int popDim, int iter) {

		this.gm = gm;
		POPULATION_SIZE = popDim;
		CHROMOSOME_LENGTH = chromLenght;
		GENERATION_LIMIT = iter;

		Individual<IntegerChromosome> sample = new Individual<IntegerChromosome>(
				new IntegerChromosome(CHROMOSOME_LENGTH, -1,
						Enviroment.DIRTY_TILES_NUMBER - 1));

		Population<IntegerChromosome> pop = new Population<IntegerChromosome>(
				sample, POPULATION_SIZE);

		Fitness<IntegerChromosome> fit = new Fitness<IntegerChromosome>(true) {

			@Override
			public void evaluate(Individual<IntegerChromosome> individual) {

				int cleanedTiles = 0;

				boolean map[] = new boolean[CHROMOSOME_LENGTH];

				ArrayList<Integer> indexes = new ArrayList<Integer>();
				for (int i = 0; i < CHROMOSOME_LENGTH; i++) {
					if (individual.getChromosome().getValue(i) != -1
							&& !map[individual.getChromosome().getValue(i)]) {
						cleanedTiles++;
						map[individual.getChromosome().getValue(i)] = true;
					}
					indexes.add(individual.getChromosome().getValue(i));
				}
				int distance = gm.computeDistance(indexes);

				if (distance == -1
						|| Enviroment.INITIAL_ENERGY - distance - cleanedTiles
								* Enviroment.SUCK_COST < 0)
					individual.setScore(0);
				else {

					final double ET = (Enviroment.INITIAL_ENERGY - distance - Enviroment.SUCK_COST
							* indexes.size());
					final double BdT = 0;
					final double E0 = Enviroment.INITIAL_ENERGY;
					final double CT = indexes.size();
					final double D0 = Enviroment.DIRTY_TILES_NUMBER;

					final Double performanceMeasure = Math.ceil((ET - BdT + 1)
							/ (E0 + 1))
							* (CT + 1) / (D0 + 1) * (1 + (ET + 1) / (E0 + 1));

					System.out.println(" ET " +ET+ " Bdt "+BdT +" E0 "+E0+ " CT "+CT+ " D0 "+ D0 + " --> Performance "+ performanceMeasure);
					
					individual.setScore(performanceMeasure);
				}
			}
		};

		ga = new GeneticAlgorithm<IntegerChromosome>(fit, pop, GENERATION_LIMIT);
		AbstractStage<IntegerChromosome> selection = new TournamentSelector<>(3);
		AbstractStage<IntegerChromosome> crossover = new Crossover<IntegerChromosome>(
				0.6) {

			@Override
			protected void cross(Individual<IntegerChromosome>[] arg0) {
				// TODO Auto-generated method stub
				IntegerChromosome child1 = arg0[0].getChromosome();
				IntegerChromosome child2 = arg0[1].getChromosome();

				double random = Math.random();

				if (random < probability) {
					// System.out.println(arg0[0]);
					// System.out.println(arg0[1]);
					int crossoverPoint = (int) (Math.random() * CHROMOSOME_LENGTH);
					int sum1 = 0;
					int sum2 = 0;
					for (int i = 0; i < CHROMOSOME_LENGTH; i++) {
						if (i < crossoverPoint) {
							if (child2.getValue(i) != -1)
								sum1 += child2.getValue(i);
						} else {
							if (child1.getValue(i) != -1)
								sum2 += child1.getValue(i);
						}
					}

					int r1 = 0;
					int r2 = 0;
					int tmp = 0;

					for (int j = 0; j < sum1; j++) {
						r1 = (int) (Math.random() * crossoverPoint);
						r2 = (int) (Math.random() * crossoverPoint);
						tmp = child1.getValue(r1);
						child1.setValue(r1, child1.getValue(r2));
						child1.setValue(r2, tmp);
					}

					for (int j = 0; j < sum2; j++) {
						r1 = (int) (Math.random() * (CHROMOSOME_LENGTH - crossoverPoint))
								+ crossoverPoint;
						r2 = (int) (Math.random() * (CHROMOSOME_LENGTH - crossoverPoint))
								+ crossoverPoint;
						tmp = child2.getValue(r1);
						child2.setValue(r1, child2.getValue(r2));
						child2.setValue(r2, tmp);
					}

					// System.out.println(arg0[0]);
					// System.out.println(arg0[1]);
					// System.out.println("_______________"+crossoverPoint);
				}

			}

			@Override
			public int spread() {
				// TODO Auto-generated method stub
				return 2;
			}
		};
		AbstractStage<IntegerChromosome> mutation = new Mutator<IntegerChromosome>(
				(double) 1 / CHROMOSOME_LENGTH) {

			@Override
			protected void mutate(Individual<IntegerChromosome> arg0) {

				double mutation = Math.random();

				if (mutation <= probability) {

					int r1 = (int) (Math.random() * CHROMOSOME_LENGTH);
					int r2 = (int) (Math.random() * CHROMOSOME_LENGTH);
					int tmp = arg0.getChromosome().getValue(r1);
					// System.out.println("mutation");
					// System.out.println(arg0);
					arg0.getChromosome().setValue(r1,
							arg0.getChromosome().getValue(r2));
					arg0.getChromosome().setValue(r2, tmp);
					// System.out.println(arg0);
					// System.out.println("________________");
				}
			}
		};
		ga.addStage(selection);
		ga.addStage(crossover);
		ga.addStage(mutation);
		ga.setElitism((int) (POPULATION_SIZE * 0.1));

		ga.addAlgorithmEventListener(new AlgorithmEventListener<IntegerChromosome>() {

			@Override
			public void onAlgorithmStop(
					GeneticAlgorithm<IntegerChromosome> arg0, long arg1) {
				// System.out.println(arg0.getInitialPopulation().getIndividuals());

			}

			@Override
			public void onAlgorithmStart(
					GeneticAlgorithm<IntegerChromosome> arg0, long arg1) {
				// TODO Auto-generated method stub

				List<Individual<IntegerChromosome>> pop = arg0
						.getInitialPopulation().getIndividuals();

				int random = 0;
				for (Individual<IntegerChromosome> i : pop) {
					boolean check[] = new boolean[CHROMOSOME_LENGTH];
					for (int j = 0; j < CHROMOSOME_LENGTH; j++) {
						random = (int) (Math.random() * (CHROMOSOME_LENGTH + 1)) - 1;
						if (random == -1) {
							if (Enviroment.INITIAL_ENERGY <= gm.matrixSize
									* gm.matrixSize * 2)
								i.getChromosome().setValue(j, -1);
							else
								j--;
						} else {
							if (!check[random]) {
								check[random] = true;
								i.getChromosome().setValue(j, random);
							} else {
								j--;
							}
						}
						// arg0.evaluateIndividual(i);
					}

				}
				// System.out.println(arg0.getInitialPopulation().getIndividuals());
			}

			@Override
			public void onAlgorithmInit(
					GeneticAlgorithm<IntegerChromosome> arg0, long arg1) {
				// System.out.println(arg0.getInitialPopulation().getIndividuals());
			}
		});

		ga.setRandomization(false);
	}

	public Individual execute() {

		ga.evolve();

		Population.Statistics stats = ga.getCurrentPopulation().getStatistics();
		Group legals = stats.getGroup(Population.LEGALS);
		Individual solution = legals.get(0);
		return solution;

	}

}
